/*
 * 从 flv/mp4 中提取出 aac 保存到本地
 */
#include <iostream>

extern "C" {
#include "libavutil/log.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
}

#define ADTS_HEADER_LEN (7)

const int samplingFrequenciesArray[] = {
    96000, //0x00
    88200,
    64000,
    48000,
    44100,
    32000, //0x05
    24000,
    22050,
    16000,
    12000,
    11025, //0x0a
    800
};

int makeAdtsHeader(char * const pAdtsHeader, const int dataLength,
                   const int profile, const int sampleRate,
                   const int channels) {
    int samplingFruquencyIndex = 3;//48000Hz
    int adtsLen = dataLength + 7;
    int frequenciesArraySize = sizeof(samplingFrequenciesArray) / sizeof(samplingFrequenciesArray[0]);
    int i = 0;

    for (; i < frequenciesArraySize; i++) {
        if (samplingFrequenciesArray[i] == sampleRate) {
            samplingFruquencyIndex = i;
            break;
        }
    }

    if (samplingFruquencyIndex > frequenciesArraySize) {
        std::cout << "Unsupport samplerate:" << sampleRate << std::endl;
        return -1;
    }

    //开始封装 adts 头信息
    pAdtsHeader[0] = 0xff;                                  //syncword:0xfff                          高8bits
    pAdtsHeader[1] = 0xf0;                                  //syncword:0xfff                          低4bits
    pAdtsHeader[1] |= (0 << 3);                             //MPEG Version:0 for MPEG-4, 1 for MPEG-2 1bit
    pAdtsHeader[1] |= (0 << 1);                             //Layer:0                                 2bits
    pAdtsHeader[1] |= 1;                                    //protection absent:1                     1bit

    pAdtsHeader[2] = (profile) << 6;                        //profile:profile                         2bits
    pAdtsHeader[2] |= (samplingFruquencyIndex & 0x0f) << 2; //sampling_frequency_index                4bits
    pAdtsHeader[2] |= (0 << 1);                             //private bit:0                           1bit
    pAdtsHeader[2] |= (channels & 0x04) >> 2;               //channel configuration:channels          高1bit

    pAdtsHeader[3] = (channels & 0x03) << 6;                //channel configuration:channels          低2bits
    pAdtsHeader[3] |= (0 << 5);                             //original: 0                             1bit
    pAdtsHeader[3] |= (0 << 4);                             //home: 0                                 1bit
    pAdtsHeader[3] |= (0 << 3);                             //copyright id bit:0                      1bit
    pAdtsHeader[3] |= (0 << 2);                             //copyright id start:0                    1bit
    pAdtsHeader[3] |= ((adtsLen & 0x1800) >> 11);           //frame length: value                     高2bits

    pAdtsHeader[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);     //frame length: value                     中间8bits
    pAdtsHeader[5] = (uint8_t)((adtsLen & 0x7) << 5);       //frame length: value                     低3bits
    pAdtsHeader[5] |= 0x1f;                                 //buffer fullness:0x7ff                   高5bits
    pAdtsHeader[6] = 0xfc;                                  //11111100,buffer fullness:0x7ff          低6bits
    //number_of_raw_data_blocks_in_frame:
    //表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧

    return 0;
}

int main(int argc, char *argv[])
{
    int ret = -1;
    char errors[1024] = {0};

    std::string inputFile, aacFile;
    FILE *aacFd = NULL;

    int audioIndex = -1;
    int len = 0;

    AVFormatContext *inputFmtCtx = NULL;
    AVPacket packet;

    //设置打印级别
    av_log_set_level(AV_LOG_DEBUG);

    if (argc < 3) {
        av_log(NULL, AV_LOG_DEBUG, "The count of parameters should be more than 3!\n");
        return -1;
    }

    inputFile = std::string(argv[1]);
    aacFile = std::string(argv[2]);

    if (inputFile.empty() || aacFile.empty()) {
        av_log(NULL, AV_LOG_DEBUG, "Src or dts is empty, plz check them!\n");
        return -1;
    }

    aacFd = fopen(aacFile.c_str(), "wb");
    if (aacFd == NULL) {
        av_log(NULL, AV_LOG_DEBUG, "Could not open destination file %s.\n", aacFile.c_str());
        return -1;
    }

    //打开输入文件
    ret = avformat_open_input(&inputFmtCtx, inputFile.c_str(), NULL, NULL);
    if (ret < 0) {
        av_strerror(ret, errors, 1024);
        av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",
               inputFile.c_str(), ret, errors);
        return -1;
    }

    //获取解码器信息
    ret = avformat_find_stream_info(inputFmtCtx, NULL);
    if (ret < 0) {
        av_strerror(ret, errors, 1024);
        av_log(NULL, AV_LOG_DEBUG, "Failed to find stream information: %s, %d(%s)\n",
               inputFile.c_str(), ret, errors);
        return -1;
    }

    //dump 媒体信息
    av_dump_format(inputFmtCtx, 0, inputFile.c_str(), 0);

    //初始化packet
    av_init_packet(&packet);

    //查找 audio 对应的 stream_index
    audioIndex = av_find_best_stream(inputFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audioIndex < 0) {
        av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %s\n",
               av_get_media_type_string(AVMEDIA_TYPE_AUDIO), inputFile);
        return AVERROR(EINVAL);
    }

    //打印 aac 级别
    std::cout << "audio profile: " << inputFmtCtx->streams[audioIndex]->codecpar->profile << " FF_PROFILE_AAC_LOW: " << FF_PROFILE_AAC_LOW << std::endl;
    if (inputFmtCtx->streams[audioIndex]->codecpar->codec_id != AV_CODEC_ID_AAC) {
        std::cout << "The media file no contain AAC stream, it's codec_id is " << inputFmtCtx->streams[audioIndex]->codecpar->codec_id << std::endl;
        goto failed_deal;
    }

    //读取媒体文件，把 aac 数据帧写到本地文件
    while (av_read_frame(inputFmtCtx, &packet) >= 0) {
        if (packet.stream_index == audioIndex) {
            char adtsHeaderBuf[7] = {0};
            makeAdtsHeader(adtsHeaderBuf, packet.size,
                           inputFmtCtx->streams[audioIndex]->codecpar->profile,
                           inputFmtCtx->streams[audioIndex]->codecpar->sample_rate,
                           inputFmtCtx->streams[audioIndex]->codecpar->channels);
            //写入 adts header，ts 流不适用，ts 流分离出来的 packet 带了 adts header
            fwrite(adtsHeaderBuf, 1, 7, aacFd);
            //写入 adts data
            len = fwrite(packet.data, 1, packet.size, aacFd);

            if (len != packet.size) {
                av_log(NULL, AV_LOG_DEBUG, "Warning, length of writed data isn't equal packet.size(%d, %d)\n", len, packet.size);
            }
        }
        av_packet_unref(&packet);
    }

failed_deal:
    if (inputFmtCtx != NULL)
        avformat_close_input(&inputFmtCtx);

    if (aacFd != NULL)
        fclose(aacFd);

    return 0;
}
